/**
 * Copyright (c) 2018-2024, NXOS Development Team
 * SPDX-License-Identifier: Apache-2.0
 * 
 * Contains: device tree from xboot
 * 
 * Change Logs:
 * Date           Author            Notes
 * 2024-1-4       JasonHu           Init
 */

#include <base/dtree.h>

#ifdef CONFIG_NX_DEVICE_TREE_SUPPORT

#include <base/defines.h>
#include <base/string.h>
#include <base/driver.h>
#include <base/initcall.h>
#define NX_LOG_NAME "dtree"
#include <base/log.h>

const char * DT_ReadName(DT_Node * n)
{
	return n ? n->name : NX_NULL;
}

int DT_ReadID(DT_Node * n)
{
	return n ? n->id : 0;
}

NX_Addr DT_ReadAddress(DT_Node * n)
{
	return n ? n->addr : 0;
}

int DT_ReadBool(DT_Node * n, const char * name, int def)
{
	struct JSON_Value * v;
	int i;

	if(n && n->value && (n->value->type == JSON_OBJECT))
	{
		for(i = 0; i < n->value->u.object.length; i++)
		{
			if(NX_StrCmp(n->value->u.object.values[i].name, name) == 0)
			{
				v = n->value->u.object.values[i].value;
				if(v && (v->type == JSON_BOOLEAN))
					return v->u.boolean ? 1 : 0;
			}
		}
	}
	return def;
}

int DT_ReadInt(DT_Node * n, const char * name, int def)
{
	struct JSON_Value * v;
	int i;

	if(n && n->value && (n->value->type == JSON_OBJECT))
	{
		for(i = 0; i < n->value->u.object.length; i++)
		{
			if(NX_StrCmp(n->value->u.object.values[i].name, name) == 0)
			{
				v = n->value->u.object.values[i].value;
				if(v && (v->type == JSON_INTEGER))
					return (int)v->u.integer;
			}
		}
	}
	return def;
}

NX_UArch DT_ReadLong(DT_Node * n, const char * name, NX_UArch def)
{
	struct JSON_Value * v;
	int i;

	if(n && n->value && (n->value->type == JSON_OBJECT))
	{
		for(i = 0; i < n->value->u.object.length; i++)
		{
			if(NX_StrCmp(n->value->u.object.values[i].name, name) == 0)
			{
				v = n->value->u.object.values[i].value;
				if(v && (v->type == JSON_INTEGER))
					return (NX_UArch)v->u.integer;
			}
		}
	}
	return def;
}

double DT_ReadDouble(DT_Node * n, const char * name, double def)
{
	struct JSON_Value * v;
	int i;

	if(n && n->value && (n->value->type == JSON_OBJECT))
	{
		for(i = 0; i < n->value->u.object.length; i++)
		{
			if(NX_StrCmp(n->value->u.object.values[i].name, name) == 0)
			{
				v = n->value->u.object.values[i].value;
				if(v && (v->type == JSON_DOUBLE))
					return (double)v->u.dbl;
			}
		}
	}
	return def;
}

char * DT_ReadString(DT_Node * n, const char * name, char * def)
{
	struct JSON_Value * v;
	int i;

	if(n && n->value && (n->value->type == JSON_OBJECT))
	{
		for(i = 0; i < n->value->u.object.length; i++)
		{
			if(NX_StrCmp(n->value->u.object.values[i].name, name) == 0)
			{
				v = n->value->u.object.values[i].value;
				if(v && (v->type == JSON_STRING))
					return (char *)v->u.string.ptr;
			}
		}
	}
	return def;
}

NX_U8 DT_ReadU8(DT_Node * n, const char * name, NX_U8 def)
{
	struct JSON_Value * v;
	int i;

	if(n && n->value && (n->value->type == JSON_OBJECT))
	{
		for(i = 0; i < n->value->u.object.length; i++)
		{
			if(NX_StrCmp(n->value->u.object.values[i].name, name) == 0)
			{
				v = n->value->u.object.values[i].value;
				if(v && (v->type == JSON_INTEGER))
					return (NX_U8)v->u.integer;
			}
		}
	}
	return def;
}

NX_U16 DT_ReadU16(DT_Node * n, const char * name, NX_U16 def)
{
	struct JSON_Value * v;
	int i;

	if(n && n->value && (n->value->type == JSON_OBJECT))
	{
		for(i = 0; i < n->value->u.object.length; i++)
		{
			if(NX_StrCmp(n->value->u.object.values[i].name, name) == 0)
			{
				v = n->value->u.object.values[i].value;
				if(v && (v->type == JSON_INTEGER))
					return (NX_U16)v->u.integer;
			}
		}
	}
	return def;
}

NX_U32 DT_ReadU32(DT_Node * n, const char * name, NX_U32 def)
{
	struct JSON_Value * v;
	int i;

	if(n && n->value && (n->value->type == JSON_OBJECT))
	{
		for(i = 0; i < n->value->u.object.length; i++)
		{
			if(NX_StrCmp(n->value->u.object.values[i].name, name) == 0)
			{
				v = n->value->u.object.values[i].value;
				if(v && (v->type == JSON_INTEGER))
					return (NX_U32)v->u.integer;
			}
		}
	}
	return def;
}

NX_U64 DT_ReadU64(DT_Node * n, const char * name, NX_U64 def)
{
	struct JSON_Value * v;
	int i;

	if(n && n->value && (n->value->type == JSON_OBJECT))
	{
		for(i = 0; i < n->value->u.object.length; i++)
		{
			if(NX_StrCmp(n->value->u.object.values[i].name, name) == 0)
			{
				v = n->value->u.object.values[i].value;
				if(v && (v->type == JSON_INTEGER))
					return (NX_U64)v->u.integer;
			}
		}
	}
	return def;
}

DT_Node * DT_ReadObject(DT_Node * n, const char * name, DT_Node * o)
{
	struct JSON_Value * v;
	int i;

	if(o && n && n->value && (n->value->type == JSON_OBJECT))
	{
		for(i = 0; i < n->value->u.object.length; i++)
		{
			if(NX_StrCmp(n->value->u.object.values[i].name, name) == 0)
			{
				v = n->value->u.object.values[i].value;
				if(v && (v->type == JSON_OBJECT))
				{
					o->name = name;
					o->addr = 0;
					o->value = v;
					return o;
				}
			}
		}
	}
	return NX_NULL;
}

int DT_ReadArrayLength(DT_Node * n, const char * name)
{
	struct JSON_Value * v;
	int i;

	if(n && n->value && (n->value->type == JSON_OBJECT))
	{
		for(i = 0; i < n->value->u.object.length; i++)
		{
			if(NX_StrCmp(n->value->u.object.values[i].name, name) == 0)
			{
				v = n->value->u.object.values[i].value;
				if(v && (v->type == JSON_ARRAY))
					return v->u.array.length;
			}
		}
	}
	return 0;
}

int DT_ReadArrayBool(DT_Node * n, const char * name, int idx, int def)
{
	struct JSON_Value * v, * e;
	int i;

	if(n && n->value && (n->value->type == JSON_OBJECT))
	{
		for(i = 0; i < n->value->u.object.length; i++)
		{
			if(NX_StrCmp(n->value->u.object.values[i].name, name) == 0)
			{
				v = n->value->u.object.values[i].value;
				if(v && (v->type == JSON_ARRAY))
				{
					if(idx >= 0 && (idx < v->u.array.length))
					{
						e = v->u.array.values[idx];
						if(e && (e->type == JSON_BOOLEAN))
							return e->u.boolean ? 1 : 0;
					}
				}
			}
		}
	}
	return def;
}

int DT_ReadArrayInt(DT_Node * n, const char * name, int idx, int def)
{
	struct JSON_Value * v, * e;
	int i;

	if(n && n->value && (n->value->type == JSON_OBJECT))
	{
		for(i = 0; i < n->value->u.object.length; i++)
		{
			if(NX_StrCmp(n->value->u.object.values[i].name, name) == 0)
			{
				v = n->value->u.object.values[i].value;
				if(v && (v->type == JSON_ARRAY))
				{
					if(idx >= 0 && (idx < v->u.array.length))
					{
						e = v->u.array.values[idx];
						if(e && (e->type == JSON_INTEGER))
							return (int)e->u.integer;
					}
				}
			}
		}
	}
	return def;
}

NX_UArch DT_ReadArrayLong(DT_Node * n, const char * name, int idx, NX_UArch def)
{
	struct JSON_Value * v, * e;
	int i;

	if(n && n->value && (n->value->type == JSON_OBJECT))
	{
		for(i = 0; i < n->value->u.object.length; i++)
		{
			if(NX_StrCmp(n->value->u.object.values[i].name, name) == 0)
			{
				v = n->value->u.object.values[i].value;
				if(v && (v->type == JSON_ARRAY))
				{
					if(idx >= 0 && (idx < v->u.array.length))
					{
						e = v->u.array.values[idx];
						if(e && (e->type == JSON_INTEGER))
							return (NX_UArch)e->u.integer;
					}
				}
			}
		}
	}
	return def;
}

double DT_ReadArrayDouble(DT_Node * n, const char * name, int idx, double def)
{
	struct JSON_Value * v, * e;
	int i;

	if(n && n->value && (n->value->type == JSON_OBJECT))
	{
		for(i = 0; i < n->value->u.object.length; i++)
		{
			if(NX_StrCmp(n->value->u.object.values[i].name, name) == 0)
			{
				v = n->value->u.object.values[i].value;
				if(v && (v->type == JSON_ARRAY))
				{
					if(idx >= 0 && (idx < v->u.array.length))
					{
						e = v->u.array.values[idx];
						if(e && (e->type == JSON_DOUBLE))
							return (double)e->u.dbl;
					}
				}
			}
		}
	}
	return def;
}

char * DT_ReadArrayString(DT_Node * n, const char * name, int idx, char * def)
{
	struct JSON_Value * v, * e;
	int i;

	if(n && n->value && (n->value->type == JSON_OBJECT))
	{
		for(i = 0; i < n->value->u.object.length; i++)
		{
			if(NX_StrCmp(n->value->u.object.values[i].name, name) == 0)
			{
				v = n->value->u.object.values[i].value;
				if(v && (v->type == JSON_ARRAY))
				{
					if(idx >= 0 && (idx < v->u.array.length))
					{
						e = v->u.array.values[idx];
						if(e && (e->type == JSON_STRING))
							return (char *)e->u.string.ptr;
					}
				}
			}
		}
	}
	return def;
}

NX_U8 DT_ReadArrayU8(DT_Node * n, const char * name, int idx, NX_U8 def)
{
	struct JSON_Value * v, * e;
	int i;

	if(n && n->value && (n->value->type == JSON_OBJECT))
	{
		for(i = 0; i < n->value->u.object.length; i++)
		{
			if(NX_StrCmp(n->value->u.object.values[i].name, name) == 0)
			{
				v = n->value->u.object.values[i].value;
				if(v && (v->type == JSON_ARRAY))
				{
					if(idx >= 0 && (idx < v->u.array.length))
					{
						e = v->u.array.values[idx];
						if(e && (e->type == JSON_INTEGER))
							return (NX_U8)e->u.integer;
					}
				}
			}
		}
	}
	return def;
}

NX_U16 DT_ReadArrayU16(DT_Node * n, const char * name, int idx, NX_U16 def)
{
	struct JSON_Value * v, * e;
	int i;

	if(n && n->value && (n->value->type == JSON_OBJECT))
	{
		for(i = 0; i < n->value->u.object.length; i++)
		{
			if(NX_StrCmp(n->value->u.object.values[i].name, name) == 0)
			{
				v = n->value->u.object.values[i].value;
				if(v && (v->type == JSON_ARRAY))
				{
					if(idx >= 0 && (idx < v->u.array.length))
					{
						e = v->u.array.values[idx];
						if(e && (e->type == JSON_INTEGER))
							return (NX_U16)e->u.integer;
					}
				}
			}
		}
	}
	return def;
}

NX_U32 DT_ReadArrayU32(DT_Node * n, const char * name, int idx, NX_U32 def)
{
	struct JSON_Value * v, * e;
	int i;

	if(n && n->value && (n->value->type == JSON_OBJECT))
	{
		for(i = 0; i < n->value->u.object.length; i++)
		{
			if(NX_StrCmp(n->value->u.object.values[i].name, name) == 0)
			{
				v = n->value->u.object.values[i].value;
				if(v && (v->type == JSON_ARRAY))
				{
					if(idx >= 0 && (idx < v->u.array.length))
					{
						e = v->u.array.values[idx];
						if(e && (e->type == JSON_INTEGER))
							return (NX_U32)e->u.integer;
					}
				}
			}
		}
	}
	return def;
}

NX_U64 DT_ReadArrayU64(DT_Node * n, const char * name, int idx, NX_U64 def)
{
	struct JSON_Value * v, * e;
	int i;

	if(n && n->value && (n->value->type == JSON_OBJECT))
	{
		for(i = 0; i < n->value->u.object.length; i++)
		{
			if(NX_StrCmp(n->value->u.object.values[i].name, name) == 0)
			{
				v = n->value->u.object.values[i].value;
				if(v && (v->type == JSON_ARRAY))
				{
					if(idx >= 0 && (idx < v->u.array.length))
					{
						e = v->u.array.values[idx];
						if(e && (e->type == JSON_INTEGER))
							return (NX_U64)e->u.integer;
					}
				}
			}
		}
	}
	return def;
}

DT_Node * DT_ReadArrayObject(DT_Node * n, const char * name, int idx, DT_Node * o)
{
	struct JSON_Value * v, * e;
	int i;

	if(o && n && n->value && (n->value->type == JSON_OBJECT))
	{
		for(i = 0; i < n->value->u.object.length; i++)
		{
			if(NX_StrCmp(n->value->u.object.values[i].name, name) == 0)
			{
				v = n->value->u.object.values[i].value;
				if(v && (v->type == JSON_ARRAY))
				{
					if(idx >= 0 && (idx < v->u.array.length))
					{
						e = v->u.array.values[idx];
						if(e && (e->type == JSON_OBJECT))
						{
							o->name = 0;
							o->addr = 0;
							o->value = e;
							return o;
						}
					}
				}
			}
		}
	}
	return NX_NULL;
}

NX_PRIVATE void ProbeAllDevices(const char * json, int length, const char * tips)
{
	NX_Driver *driver;
	NX_Device *device;
	
	DT_Node n;
	struct JSON_Value * v;
	char errbuf[256];

	if(json && (length > 0))
	{
		v = JSON_Parse(json, length, errbuf);
		if(v && (v->type == JSON_OBJECT))
		{
			for(int i = 0; i < v->u.object.length; i++)
			{
				char *  p = (char *)(v->u.object.values[i].name);
				if(p)
				{
					char * name = p;
					char * id = NX_NULL;
					char * addr = NX_NULL;
					while(*p)
					{
						if(*p == ':')
						{
							*p = '\0';
							id = p + 1;
							if((*id == ':') || (*id == '@'))
								id = NX_NULL;
						}
						else if(*p == '@')
						{
							*p = '\0';
							addr = p + 1;
							if((*addr == ':') || (*addr == '@'))
								addr = NX_NULL;
						}
						p++;
					}
					n.name = (name && (*name != '\0')) ? name : "";
					n.id = (id && (*id != '\0')) ? NX_StrToUL(id, NX_NULL, 0) : 0;
					n.addr = (addr && (*addr != '\0')) ? NX_StrToUL(addr, NX_NULL, 0) : 0;
					n.value = (struct JSON_Value *)(v->u.object.values[i].value);

					if(NX_StrCmp(DT_ReadString(&n, "status", "okay"), "disabled") != 0)
					{
						char *compatible = DT_ReadString(&n, "compatible", "none");

						if (!NX_StrCmp(compatible, "none"))
						{
							continue;
						}

						driver = NX_DriverMatch(compatible, &n);
						if (driver != NX_NULL)
						{
							device = driver->probe(driver, &n);
							if (device != NX_NULL)
							{
								NX_LOG_I("[Success] Probe device '%s' with driver %s", device->name, driver->name);
							}
							else
							{
								NX_LOG_E("[Failed] Probe device node %s", n.name);
							}
						}
					}
				}
			}
		}
		else
		{
			NX_LOG_D("[%s]-%s\r\n", tips ? tips : "Json", errbuf);
		}
		JSON_Free(v);
	}
}

NX_IMPORT NX_U8 __NX_DevTreeStart[];
NX_IMPORT NX_U8 __NX_DevTreeEnd[];

NX_PRIVATE void NX_ProbeAllDevicesInit(void)
{
    NX_LOG_D("json: start=%p, end=%p", __NX_DevTreeStart, __NX_DevTreeEnd);

	if (__NX_DevTreeEnd == __NX_DevTreeStart)
	{
		NX_LOG_D("no json device tree inside kernel!\n");
		return;
	}
    ProbeAllDevices((const char *)__NX_DevTreeStart, (int)(__NX_DevTreeEnd - __NX_DevTreeStart), NX_NULL);
}

NX_FINAL_INIT(NX_ProbeAllDevicesInit);
#endif
